//============================================================================
// Name        : Prisons_map.h
// Author      : Andrey Bicalho Santos e Clayson Sandro Celes
// Version     :
// Copyright   : if you are making money with it... please give me some, because I have no money!
// Description : Genetic Algorithm in C++, Ansi-style
//============================================================================

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <cstring>
#include <math.h>
#include "Ant.h"
#include "CImg.h"

#define TAM_MAX_LINHA 1024

using namespace std;
using namespace cimg_library;

void pera(){
	cout << "\nOi\n";
	string x;
	cin >> x;
}

string *load_map(FILE *fl, int *num_linhas_mapa);
int count_lines(FILE *fl);
int **get_reduced_map(int **mapa,Point dim_map,Point *dim_reduced_map);
string *build_final_map(int **labirinto,Point dim_labirinto,int **solution_map,Point dim_solution_map);
int **preProcessing(int **mapa,Point dim_map);
int **copy_map(int **map,Point dim_map);
void clone(int **origem,int **destino,Point dim_map);
int **str_map2bin_map(string map[], int num_linhas,Point *dim_labirinto);
int get_number_of_valid_space(int **map, Point dim_map);
bool is_wall(int **map,int x, int y);
bool is_start(int **map,int x, int y);
bool is_end(int **map,int x, int y);
bool is_in(Point current_point, Point dim_map);
bool is_inside(int x,int y,Point dim_map);
Point FindStartPoint(int **map,Point dim_map);
Point FindEndPoint(int **map,Point dim_map);
int count_good_genes(int **solution_map,int **map,Point dim_map);
int count_erros(int **solution_map,int **map,Point dim_map);
void printMapa(int **map,Point dim_map);
void printPheromMapa(double **map,Point dim_map);
double euclidian_distance_2D(Point ponto1,Point ponto2);

CImg<int> update_display(Ant *formigas, int index,double **pherom_map,double taxa_evaporacao,int **map,Point dim,CImg<int> cimg_labirinto);

//------------------------------------------------------------------------------
double getAverage(Ant *formigas,int num_formigas){
	double ave = 0;
	int n_f = num_formigas;

	for(int i=0;i<num_formigas;i++){
		if(formigas[i].get_tamanho_do_caminho() == 0)
			n_f--;
		else
			ave += formigas[i].get_tamanho_do_caminho();
	}

	if(n_f <= 0)
		return 0;
	ave = ave/n_f;

	return ave;
}
//------------------------------------------------------------------------------
double getVariance(Ant *formigas,int num_formigas){
	double average = getAverage(formigas,num_formigas);
	double variance = 0;
	int n_f = num_formigas;


	for(int i=0;i<num_formigas;i++){
		if(formigas[i].get_tamanho_do_caminho() == 0)
			n_f--;
		else
			variance += pow((formigas[i].get_tamanho_do_caminho() - average),2);
	}

	if(n_f-1 <= 0)
		return 0;
	variance = variance/(n_f-1);

	return variance;
}
//------------------------------------------------------------------------------
double getStdDev(Ant *formigas,int num_formigas){
	double stddev = getVariance(formigas,num_formigas);

	return sqrt(stddev);
}
//------------------------------------------------------------------------------
int getSmallestPathID(Ant *formigas,int num_formigas){
	int small_path = 999999999,id = 0;

	for(int i=0;i<num_formigas;i++){
		if(formigas[i].get_tamanho_do_caminho() < small_path && formigas[i].get_tamanho_do_caminho() != 0){
			small_path = formigas[i].get_tamanho_do_caminho();
			id = i;
		}
	}

	return id;
}
//-------------------------------------------------------------------------------------------------------------
int getSmallestPath(Ant *formigas,int num_formigas){
	double small_path = 999999999;

	for(int i=0;i<num_formigas;i++){
		if(formigas[i].get_tamanho_do_caminho() < small_path && formigas[i].get_tamanho_do_caminho() != 0){
			small_path = formigas[i].get_tamanho_do_caminho();
		}
	}

	return small_path;
}
//-------------------------------------------------------------------------------------------------------------
int count_repeated_place(Ant *formigas,int k){

	/*if (formigas[k].get_tamanho_do_caminho() > 0) {
		int tam = formigas[k].get_stack_size(), repeated_place = 0, i = 0;
		Point *temp;
		temp = formigas[k].get_stack();

		for (i = 0; i < tam; i++)
			for (int j = i; j < tam; j++)
				if (temp[j].x == temp[i].x && temp[j].y == temp[i].y)
					repeated_place++;

		delete[] temp;
		return repeated_place;
	}*/

	/*if (formigas[k].get_tamanho_do_caminho() > 0) {

		stack<Point> mystack = formigas[k].get_mystack();
		int tam = mystack.size(), repeated_place = 0, i = 0;
		Point *temp;
		temp = new Point[tam];

		while(!mystack.empty()){
			temp[i] = mystack.top();
			mystack.pop();
			i++;
		}

		for (i = 0; i < tam; i++)
			for (int j = i; j < tam; j++)
				if (temp[j].x == temp[i].x && temp[j].y == temp[i].y)
					repeated_place++;

		delete[] temp;
		return repeated_place;
	}*/

	return 0;
}
//-------------------------------------------------------------------------------------------------------------
string *load_map(FILE *fl, int *num_linhas_mapa){
    string *mapa;
    *num_linhas_mapa = count_lines(fl) - 1;
    int i_linha = 0;
    mapa = new string[*num_linhas_mapa];
    char line_temp[TAM_MAX_LINHA];
    rewind(fl);
    while(fgets(line_temp,TAM_MAX_LINHA,fl) != NULL){
        int z = 0;
        while(line_temp[z] != '\r' && line_temp[z] != '\n'){line_temp[z] = tolower(line_temp[z]); z++;}
        line_temp[z] = '\0';
        mapa[i_linha] = line_temp;
        i_linha++;
    }
    return mapa;
}
//------------------------------------------------------------------------------
//conta a quantidade de linhas do arquivo
int count_lines(FILE *fl){
    int linhas = 1;
    char c;
    rewind(fl);

    do{
         c = fgetc(fl);
         if(c == '\n')
           linhas++;

    }while(c != EOF);
    rewind(fl);
    return linhas;
}
//------------------------------------------------------------------------------
int **get_reduced_map(int **mapa,Point dim_map,Point *dim_reduced_map){
	int new_dimy = (dim_map.y+1)/2, **new_map;

	new_map = new int*[dim_map.x];
	for(int i=0;i < dim_map.x;i++)
		new_map[i] = new int[new_dimy];

	bool sequence3_0 = false;
	int cont;
	for(int i=0;i<dim_map.x;i++){
		cont = 0;
		sequence3_0 = false;
		for(int j=0;j<dim_map.y;j++){
			if(j<(dim_map.y-2)){
				if(mapa[i][j] == 9){
					new_map[i][cont] = 9;
					sequence3_0=false;
					//cont++;
				}
				else if(mapa[i][j] == 1 && mapa[i][j+1] == 1 && mapa[i][j+2] == 1){
					j = j+2;
					new_map[i][cont] = 1;
					//cont++;
				}
				else if(mapa[i][j] == 0 && mapa[i][j+1] == 0 && mapa[i][j+2] == 0 && sequence3_0==false){
					j = j+2;
					new_map[i][cont] = 0;
					sequence3_0 = true;
					//cont++;
				}
				else if(mapa[i][j] == 1){
					sequence3_0=false;
					new_map[i][cont] = 1;
					//cont++;
				}
				else if(mapa[i][j] == 0 && mapa[i][j+1] == 3 && mapa[i][j+2] == 0){
					j = j+2;
					new_map[i][cont] = 3;
					//cont++;
				}
				else if(mapa[i][j] == 0 && mapa[i][j+1] == 5 && mapa[i][j+2] == 0){
					j = j+2;
					new_map[i][cont] = 5;
				}
				/*else if(mapa[i][j] == 3 && mapa[i][j+1] == 0){
					j = j+2;
					new_map[i][cont] = 3;
					//cont++;
				}
				else if(mapa[i][j] == 5 && mapa[i][j+1] == 0){
					j = j+2;
					new_map[i][cont] = 5;
				}*/
				else if(mapa[i][j] == 0 && sequence3_0 == true){
					sequence3_0 = false;
					new_map[i][cont] = 0;
					//printf("\ninvalido!\n");
				}
				cont++;
			}
			else{
				if(mapa[i][j] == 5)
					new_map[i][cont] = 5;
				else if(mapa[i][j] == 3)
					new_map[i][cont] = 3;
				else{

				}
			}
		}
		new_map[i][new_dimy-1] = 9;
	}

	dim_reduced_map->x = dim_map.x;
	dim_reduced_map->y = new_dimy;

	return new_map;
}
//------------------------------------------------------------------------------
string *build_final_map(int **labirinto,Point dim_labirinto,int **solution_map,Point dim_solution_map){

	string *mapa;
	mapa = new string[dim_labirinto.x];

	char *temp;
	temp = new char[dim_labirinto.y+1];

	int cont;
	bool sequence3_0 = false;

	for(int i=0;i<dim_solution_map.x;i++){
		cont = 0;
		sequence3_0 = false;
		for(int j=0;j<dim_solution_map.y;j++){
				if(solution_map[i][j] == 4){
					labirinto[i][cont] = 4;
					labirinto[i][cont+1] = 4;
					labirinto[i][cont+2] = 4;
					cont = cont + 2;
					sequence3_0 = true;
				}
				else if(solution_map[i][j] == 4 && sequence3_0 == true){
					labirinto[i][cont] = 4;
					sequence3_0 = false;
				}

				cont++;
		}

	}


/*
		for(int i=0;i<dim_labirinto.x;i++){
			for(int j=0;i<dim_labirinto.y;i++){
				if(labirinto[i][j] == 9)
					temp[j] = '+';
				else if(labirinto[i][j] == )
			}
		}*/
		printf("\nbuild final map\n");
		printMapa(labirinto,dim_labirinto); pera();

	return mapa;
}
//------------------------------------------------------------------------------
int **preProcessing(int **mapa,Point dim_map){
	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			if(mapa[i][j] == 9)
				mapa[i][j] = 1;

	return mapa;
}
//------------------------------------------------------------------------------
int **copy_map(int **map,Point dim_map){
	int **copy;
	copy = new int*[dim_map.x];
	for(int i=0;i < dim_map.x;i++)
		copy[i] = new int[dim_map.y];

	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			copy[i][j] = map[i][j];

	return copy;
}
//------------------------------------------------------------------------------
void clone(int **origem,int **destino,Point dim_map){
	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			destino[i][j] = origem[i][j];
}
//------------------------------------------------------------------------------
int get_number_of_valid_space(int **map, Point dim_map){
	int valid_space = 0;
	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			if(map[i][j] == 0)
				valid_space++;

	return valid_space;
}
//------------------------------------------------------------------------------
int **str_map2bin_map(string map[], int num_linhas,Point *dim_labirinto){
	int dimx, dimy;
	dimx = num_linhas;
	dimy = map[0].length(); //assume-se q todas as linhas possuem o mesmo tamanho...
	int **bin_map;
	//cout << dimy << endl; string x; cin >> x;

	//criando matriz dinamicamente
	bin_map = new int*[dimx];
	for(int i=0;i < dimx;i++)
		bin_map[i] = new int[dimy];

	char *temp = new char[dimy+1];

	for(int i=0;i < dimx;i++){
		strcpy(temp,map[i].c_str());
		for(int j=0;j < dimy;j++){
			if(temp[j] == ' ')
				bin_map[i][j] = 0;
			else if(temp[j] == 's')
				bin_map[i][j] = 5;
			else if(temp[j] == 'e')
				bin_map[i][j] = 3;
			else if(temp[j] == '+')
				bin_map[i][j] = 9;
			else
				bin_map[i][j] = 1;
		}
	}

	dim_labirinto->x = dimx;
	dim_labirinto->y = dimy;
	delete[] temp;

	return bin_map;
}
//------------------------------------------------------------------------------
bool is_wall(int **map,int x, int y){
	if(map[x][y] == 1)
			return true;

	return false;
}
//------------------------------------------------------------------------------
bool is_start(int **map,int x, int y){
	if(map[x][y] == 5)
		return true;

	return false;
}
//------------------------------------------------------------------------------
bool is_end(int **map,int x, int y){
	if(map[x][y] == 3)
			return true;

	return false;
}
//------------------------------------------------------------------------------
bool is_in(Point current_point, Point dim_map){
	if((current_point.x >= 0 && current_point.x < dim_map.x) && (current_point.y >= 0 && current_point.y < dim_map.y))
		return true;

	return false;
}
//------------------------------------------------------------------------------
bool is_inside(int x,int y,Point dim_map){
	if((x >= 0 && x < dim_map.x) && (y >= 0 && y < dim_map.y))
		return true;

	return false;
}
//------------------------------------------------------------------------------
Point FindStartPoint(int **map,Point dim_map){
	Point start;
	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			if(map[i][j] == 5){
				start.x = i;
				start.y = j;
				return start;
			}

	return start;
}
//------------------------------------------------------------------------------
Point FindEndPoint(int **map,Point dim_map){
	Point end;
	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			if(map[i][j] == 3){
				end.x = i;
				end.y = j;
				return end;
			}

	return end;
}
//------------------------------------------------------------------------------
int count_good_genes(int **solution_map,int **map,Point dim_map){
	int count = 0;
	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			if(solution_map[i][j] == 4 && map[i][j] == 0)
				count++;

	return count*2;
}
//------------------------------------------------------------------------------
int count_erros(int **solution_map,int **map,Point dim_map){
	int count = 0;
	for(int i=0;i<dim_map.x;i++)
		for(int j=0;j<dim_map.y;j++)
			if(solution_map[i][j] == 4 && map[i][j] == 1)
				count++;

	return count;

}
//------------------------------------------------------------------------------
void printMapa(int **map,Point dim_map){
	printf("\n");
	for(int i=0;i<dim_map.x;i++){
		for(int j=0;j<dim_map.y;j++)
			printf("%d",map[i][j]);
		printf("\n");
	}
}
//------------------------------------------------------------------------------
void printPheromMapa(double **map,Point dim_map){
	printf("\n");
	for(int i=0;i<dim_map.x;i++){
		for(int j=0;j<dim_map.y;j++)
			printf(" %1.2f",map[i][j]);
		printf("\n");
	}
}
//------------------------------------------------------------------------------
double euclidian_distance_2D(Point ponto1,Point ponto2){
	double distance = 0.0;

	distance = pow((ponto1.x - ponto2.x),2) + pow((ponto1.y - ponto2.y),2);

	return sqrt(distance);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//atualiza display
CImg<int> update_display(Ant *formigas, int num_formigas,double **pherom_map,double taxa_evaporacao,int **map,Point dim,CImg<int> cimg_labirinto){

	//mapa limpo... somente labirinto, nest e food
	cimg_forXY(cimg_labirinto,x,y){
		if(map[x][y] == 1) //wall - preto
			cimg_labirinto(y,x,0) = cimg_labirinto(y,x,1) = cimg_labirinto(y,x,2) = 0;
		else if(map[x][y] == 0)//free space - branco
			cimg_labirinto(y,x,0) = cimg_labirinto(y,x,1) = cimg_labirinto(y,x,2) = 255;
		else if(map[x][y] == 5){//nest - vermelho
			cimg_labirinto(y,x,0) = 255;
			cimg_labirinto(y,x,1) = 0;
			cimg_labirinto(y,x,2) = 0;
		}
		else if(map[x][y] == 3){//food - verde
			cimg_labirinto(y,x,0) = 0;
			cimg_labirinto(y,x,1) = 255;
			cimg_labirinto(y,x,2) = 0;
		}
		else{}

	}


	//evaporacao do feromonio
	for(int i=0;i<dim.x;i++)
		for(int j=0;j<dim.y;j++){
			//pherom_map[i][j] =  (1 - taxa_evaporacao) * pherom_map[i][j];
			pherom_map[i][j] = pherom_map[i][j] - taxa_evaporacao;
			if(pherom_map[i][j] <= 0)
				pherom_map[i][j] = 0;
		}

	/*for (int i = 0; i < dim.x; i++) {
		for (int j = 0; j < dim.y; j++) {
			pherom_map[i][j] = (1-taxa_evaporacao)*pherom_map[i][j] + pherom_values[i][j];
		}
	}*/


	//atualizando rastro de feromonio no mapa do display - vermelho
	double temp = 0;
	int R = 255;
	Point pt;

	cimg_forXY(cimg_labirinto,x,y){
		if(map[x][y] == 0){
			temp = 255 - (pherom_map[x][y] * 255); //cor cyan...
			R = temp;
			cimg_labirinto(y,x,0) = R;
			cimg_labirinto(y,x,1) = 255;
			cimg_labirinto(y,x,2) = 255;
		}
	}

	//atualizando a posicao das formigas no mapa do display
	for(int i=0;i<num_formigas;i++){
		pt = formigas[i].get_position();
		if(!formigas[i].get_behavior().searching_food && formigas[i].get_behavior().returning_nest && formigas[i].get_behavior().carring_food){ //verde
			cimg_labirinto(pt.y,pt.x,0) = 0;
			cimg_labirinto(pt.y,pt.x,1) = 255;
			cimg_labirinto(pt.y,pt.x,2) = 0 ;
		}
		else if(formigas[i].get_behavior().searching_food && !formigas[i].get_behavior().returning_nest && !formigas[i].get_behavior().carring_food){ //vermelho
			cimg_labirinto(pt.y,pt.x,0) = 255;
			cimg_labirinto(pt.y,pt.x,1) = cimg_labirinto(pt.y,pt.x,2) = 0 ;

		}
		else if(formigas[i].get_behavior().searching_food && formigas[i].get_behavior().returning_nest && !formigas[i].get_behavior().carring_food){ //amarelo
			cimg_labirinto(pt.y,pt.x,0) = 255;
			cimg_labirinto(pt.y,pt.x,1) = 255;
			cimg_labirinto(pt.y,pt.x,2) = 0;

		}
		else{
			cout << "\nProblemas em MazeProblem.h : CImg<int> update_display(Ant*,int,double**,double,int**,Point,CImg<int>)\n";
		}
	}


	return cimg_labirinto;
}
//------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
